package core

import (
	"errors"
	"fmt"
	"go-iot/app/iot/tsl"
	"strings"
	"go-iot/app/common/logger"
)

// base context
type BaseContext struct {
	DeviceId  string  `json:"deviceId"`
	ProductId string  `json:"productId"`
	Session   Session `json:"-"`
	device    *Device `json:"-"`
}

// 设备上线，调用后设备状态改为上线
func (ctx *BaseContext) DeviceOnline(deviceId string) {
	deviceId = strings.TrimSpace(deviceId)
	if len(deviceId) > 0 {
		oldSession := GetSession(deviceId)
		replace := false
		if oldSession != nil && oldSession != ctx.GetSession() {
			replace = true
			logger.Infof("device [%s] a new connection come in, close old session", deviceId)
			oldSession.Close()
		}
		device := GetDevice(deviceId)
		if device == nil {
			logger.Warnf("device [%s] not exist or noActive, close session", deviceId)
			ctx.GetSession().Close()
			return
		}
		ctx.DeviceId = deviceId
		ctx.GetSession().SetDeviceId(deviceId)
		SetSession(deviceId, ctx.GetSession(), replace)
	}
}

// 获取Device，需要在DeviceOnline后才会有值
func (ctx *BaseContext) GetDevice() *Device {
	if ctx.device != nil {
		return ctx.device
	}
	ctx.device = ctx.GetDeviceById(ctx.DeviceId)
	return ctx.device
}

// 设置Context中的Device，对于无状态连接可以先使用GetDeviceById，然后调用SetDevice来设置
func (ctx *BaseContext) SetDevice(d *Device) {
	ctx.device = d
	ctx.DeviceId = d.Id
}

// 通过deviceId获取Device,在无状态连接时有帮助
func (ctx *BaseContext) GetDeviceById(deviceId string) *Device {
	d := GetDevice(deviceId)
	return d
}

func (ctx *BaseContext) GetProduct() *Product {
	if len(ctx.ProductId) == 0 {
		return nil
	}
	return GetProduct(ctx.ProductId)
}

func (ctx *BaseContext) GetSession() Session {
	return ctx.Session
}

func (ctx *BaseContext) GetMessage() any {
	return nil
}

// 获取产品配置
func (ctx *BaseContext) GetConfig(key string) string {
	device := ctx.GetDevice()
	if device == nil {
		return ""
	}
	return device.GetConfig(key)
}


func (ctx *BaseContext) GetProperties(key string) any {
	p := ctx.GetProduct()
	if p == nil {
		return nil
	}
	if ctx.DeviceId == "" {
		return nil
	}
	
	param := TimeDataSearchRequest{
		DeviceId: ctx.DeviceId,
		PageNum:  1,
		PageSize: 1,  // 限制只返回一条数据
		Condition: []SearchTerm{
			{
				Key:   tsl.PropertyDeviceId,
				Value: ctx.DeviceId,
				Oper:  EQ,
			},
		},
	}
	
	data, err := p.GetTimeSeries().QueryProperty(p, param)
	if err != nil {
		return nil
	}


	list, ok := data["list"]
	if !ok {
		return nil
	}
	
	// 将list转换为[]map[string]interface{}
	listSlice, ok := list.([]map[string]interface{})
	if !ok || len(listSlice) == 0 {
		return nil
	}
	
	// 只返回第一条数据
	return listSlice[0]
}

// 保存设备属性的时序数据
func (ctx *BaseContext) SetProperties(data map[string]any) {
	p := ctx.GetProduct()
	if p == nil {
		logger.Warnf("product [%s] not exist or noActive", ctx.ProductId)
		return
	}
	if _, ok := data[tsl.PropertyDeviceId]; !ok {
		data[tsl.PropertyDeviceId] = ctx.DeviceId
	}
	if data[tsl.PropertyDeviceId] == "" {
		logger.Warnf("SetProperties error: deviceId is empty")
		return
	}
	err := p.GetTimeSeries().SetProperties(p, data)
	if err != nil {
		logger.Errorf("SetProperties error: %v", err)
		DebugLog(fmt.Sprintf("%v", data[tsl.PropertyDeviceId]), ctx.ProductId, "SetProperties error: "+err.Error())
	}
}

// 保存设备事件的时序数据
func (ctx *BaseContext) SetEvents(eventId string, data any) {
	p := ctx.GetProduct()
	if p == nil {
		logger.Warnf("product [%s] not exist or noActive", ctx.ProductId)
		return
	}
	data1 := map[string]any{}
	switch d := data.(type) {
	case map[string]any:
		data1 = d
	default:
		data1[eventId] = data
	}
	if _, ok := data1[tsl.PropertyDeviceId]; !ok {
		data1[tsl.PropertyDeviceId] = ctx.DeviceId
	}
	if data1[tsl.PropertyDeviceId] == "" {
		panic(errors.New("SetEvents error: deviceId is empty"))
	}
	err := p.GetTimeSeries().SetEvents(p, eventId, data1)
	if err != nil {
		logger.Errorf("SetEvents error: %v", err)
		DebugLog(fmt.Sprintf("%v", data1[tsl.PropertyDeviceId]), ctx.ProductId, "SetEvents error: "+err.Error())
	}
}

func (ctx *BaseContext) ReplyOk() {
	replyMap.reply(ctx.DeviceId, &FuncInvokeReply{Success: true})
}

func (ctx *BaseContext) ReplyFail(resp string) {
	replyMap.reply(ctx.DeviceId, &FuncInvokeReply{Success: false, Msg: resp})
}

// 异步消息的回复
func (ctx *BaseContext) ReplyAsync(resp map[string]any) {
	reply := &FuncInvokeReply{Success: true}
	msg, ok := resp["msg"]
	if ok {
		reply.Msg = fmt.Sprintf("%v", msg)
	}
	if fmt.Sprintf("%v", resp["success"]) == "false" {
		reply.Success = false
	}
	traceId, ok := resp["traceId"]
	if ok {
		reply.TraceId = fmt.Sprintf("%v", traceId)
	}
	replyLogAsync(ctx.GetProduct(), ctx.DeviceId, reply)
}